package com.feng.code;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Data
@ConfigurationProperties(prefix = "mybatis-plus.code")
public class Generator {
    /**
     * 开启代码生成
     */
    private boolean enable = false;
    /**
     * 由druid数据源注入,不使用druid需要自己注入
     */
    @Value("${spring.datasource.druid.url:a}")
    private String url;
    /**
     * 由druid数据源注入,不使用druid需要自己注入
     */
    @Value("${spring.datasource.druid.username:a}")
    private String username;
    /**
     * 由druid数据源注入,不使用druid需要自己注入
     */
    @Value("${spring.datasource.druid.password:a}")
    private String password;
    /**
     * 逻辑删除字段名，与数据库字段一致
     */
    private String deleted = "deleted";
    /**
     * 作者
     */
    private String author = "thyme";
    /**
     * 开启swagger,只生成注解，
     */
    private boolean swagger = false;
    /**
     * 父包名
     */
    private String parent = "com.java";
    /**
     * 模块名
     */
    private String moduleName = "module";
    /**
     * 主键生成策略,默认自增
     */
    private IdType idType = IdType.AUTO;
    /**
     * 表名
     */
    private String[] table;

    public void generatorCode() {
        if (table == null) {
            throw new NullPointerException("Table name cannot be empty");
        }
        //1、配置数据源
        FastAutoGenerator.create(url, username, password)
                //2、全局配置
                .globalConfig(builder -> {
                    builder.author(author) // 设置作者名
                            .outputDir(System.getProperty("user.dir") + "/src/main/java")   //设置输出路径：项目的 java 目录下
                            .commentDate("yyyy-MM-dd HH:mm:ss")   //注释日期
                            .dateType(DateType.ONLY_DATE)   //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
                            .fileOverride()   //覆盖之前的文件
                            .disableOpenDir();   //禁止打开输出目录，默认打开
                    if (swagger) {
                        builder.enableSwagger();
                    }
                })
                //3、包配置
                .packageConfig(builder -> {
                    builder.parent(parent) // 设置父包名
                            .moduleName(moduleName)   //设置模块包名
                            .entity("entity")   //pojo 实体类包名
                            .service("service") //Service 包名
                            .serviceImpl("service.impl") // ***ServiceImpl 包名
                            .mapper("mapper")   //Mapper 包名
                            .xml("mapper")  //Mapper XML 包名
                            .controller("controller") //Controller 包名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper"));    //配置 mapper.xml 路径信息：项目的 resources 目录下
                })
                //4、策略配置
                .strategyConfig(builder -> {
                    builder.addInclude(table) // 设置需要生成的数据表名
                            .addTablePrefix(getTablePrefix(table)) // 设置过滤表前缀
                            //4.1、Mapper策略配置
                            .mapperBuilder()
                            .superClass(BaseMapper.class)   //设置父类
                            .formatMapperFileName("%sMapper")   //格式化 mapper 文件名称
                            .enableMapperAnnotation()       //开启 @Mapper 注解
                            .formatXmlFileName("%sMapper") //格式化 Xml 文件名称

                            //4.2、service 策略配置
                            .serviceBuilder()
                            .formatServiceFileName("%sService") //格式化 service 接口文件名称，%s进行匹配表名，如 UserService
                            .formatServiceImplFileName("%sServiceImpl") //格式化 service 实现类文件名称，%s进行匹配表名，如 UserServiceImpl

                            //4.3、实体类策略配置
                            .entityBuilder()
                            .enableLombok() //开启 Lombok
                            .disableSerialVersionUID()  //不实现 Serializable 接口，不生产 SerialVersionUID
                            .logicDeleteColumnName(deleted)   //逻辑删除字段名
                            .naming(NamingStrategy.underline_to_camel)  //数据库表映射到实体的命名策略：下划线转驼峰命
                            .columnNaming(NamingStrategy.underline_to_camel)    //数据库表字段映射到实体的命名策略：下划线转驼峰命
                            .addTableFills(
                                    new Column("create_time", FieldFill.INSERT),
                                    new Column("update_time", FieldFill.INSERT_UPDATE)
                            )   //添加表字段填充，"create_time"字段自动填充为插入时间，"update_time"字段自动填充为插入修改时间
                            .enableTableFieldAnnotation()       // 开启生成实体时生成字段注解
                            .enableChainModel()           //  开启链式模型
                            .idType(idType)

                            //4.4、Controller策略配置
                            .controllerBuilder()
                            .formatFileName("%sController") //格式化 Controller 类文件名称，%s进行匹配表名，如 UserController
                            .enableRestStyle();  //开启生成 @RestController 控制器
                })
                //5、模板
                .templateEngine(new FreemarkerTemplateEngine())
                //6、执行
                .execute();
    }


    /**
     * 获得表的所有前缀
     * example
     * "t_w_aa", t_w_
     * "t_user" t_
     * "user"  remove
     *
     * @param table
     * @return
     */
    private String[] getTablePrefix(String... table) {
        List<String> list = new ArrayList<>();
        Arrays.stream(table)
                .map(item -> item.substring(0, item.lastIndexOf("_") + 1))
                .filter(item -> item.length() > 0)
                .forEach(list::add);
        return list.stream().toArray(value -> new String[list.size()]);
    }
}

